﻿////////////////////////////////////////////////////////////////////////////////////////////////////
// file:	src\LuceneIndexer.cs
//
// summary:	Implements the lucene indexer class
////////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
//using StandardAnalyzer = org.apache.lucene.analysis.standard.StandardAnalyzer;
//using IndexWriter = org.apache.lucene.index.IndexWriter;
//using org.apache.lucene.analysis.standard;
//using org.apache.lucene.index;
//using org.apache.lucene.document;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Documents;
using Lucene.Net.Search;
using Lucene.Net.QueryParsers;
using Lucene.Net.Analysis.Snowball;
//using org.apache.lucene.search;
//using org.apache.lucene.analysis;
//using org.apache.lucene.queryParser;

using PdfLucene.src;

namespace PdfLucene
{
    public class LuceneIndexer
    {
        public static string INDEXFIELD_FILENAME = "fileName";
        public static string INDEXFIELD_CONTENTS = "contents";
        public static string INDEXFIELD_ABSTRACT = "abstract";
        public static string INDEXFIELD_PATH = "path";
        public static string INDEXFIELD_MODIFIEDTIME = "modifiedtime";
        public static string INDEXFIELD_NOTE = "note";



        private IndexWriter indexWriter;
        private string indexDirectory;
        private bool indexOpen;

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Gets or sets the pathname of the index directory. </summary>
        ///
        /// <value> The pathname of the index directory. </value>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public string IndexDirectory
        {
            get
            {
                return this.indexDirectory;
            }
            set
            {
                this.indexDirectory = value;
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Index documents. </summary>
        ///
        /// <remarks>   Ubicuolab, 4/5/2010. </remarks>
        ///
        /// <param name="pdfFiles"> The pdf files. </param>       
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        
        public void IndexDocuments(List<PDFFile> pdfFiles)
        {
            if (indexOpen == true)
            {
                try
                {
                    foreach (PDFFile pdfFile in pdfFiles)
                    {
                        this.indexWriter.AddDocument(CreateDocument(pdfFile));
                    }

                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {

                }
                indexWriter.Optimize();
            }
            else
                throw new ApplicationException("Index is not open, call CreateNewIndexFile() or ContinueIndexFile() first");
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Index document. </summary>
        ///
        /// <remarks>   Ubicuolab, 4/5/2010. </remarks>
        ///
        /// <param name="pdfFile">  The pdf file. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public void IndexDocument(PDFFile pdfFile)
        {
            if (indexOpen == true)
            {
                this.indexWriter.AddDocument(CreateDocument(pdfFile));
                indexWriter.Optimize();
            }
            else
                throw new ApplicationException("Index is not open, call CreateNewIndexFile() or ContinueIndexFile() first");
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Searches for the first index by content. </summary>
        ///
        /// <remarks>   Ubicuolab, 4/5/2010. </remarks>
        ///
        /// <param name="stringQuery">  The string query. </param>
        ///
        /// <returns>   The found index by content. </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public static List<PdfSearchResult> SearchIndexByContent(string stringQuery, QueryExpansions expansionType)
        {
            return SearchIndex(stringQuery, INDEXFIELD_CONTENTS, expansionType);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Searches for the first index by abstract. </summary>
        ///
        /// <remarks>   Ubicuolab, 4/5/2010. </remarks>
        ///
        /// <param name="stringQuery">  The string query. </param>
        ///
        /// <returns>   The found index by abstract. </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public static List<PdfSearchResult> SearchIndexByAbstract(string stringQuery, QueryExpansions expansionType)
        {
            return SearchIndex(stringQuery, INDEXFIELD_ABSTRACT,expansionType);
        }
        public static List<PdfSearchResult> SearchIndexByNote(string stringQuery, QueryExpansions expansionType)
        {
            return SearchIndex(stringQuery, INDEXFIELD_NOTE,expansionType);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Searches for the first index. </summary>
        ///
        /// <remarks>   Ubicuolab, 4/5/2010. </remarks>
        ///
        /// <param name="stringQuery">  The string query. </param>
        /// <param name="WordNet">        The WordNet. </param>
        ///
        /// <returns>   The found index. </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        private static List<PdfSearchResult> SearchIndex(string stringQuery, string field, QueryExpansions expansionType)
        {
            List<PdfSearchResult> searchResuls = new List<PdfSearchResult>();

            Searcher searcher = new IndexSearcher(Constantes.INDEX_DIRECTORY);
            Searcher wordNetSearcher = null;
            Analyzer analyzer = new StandardAnalyzer(StopAnalyzer.ENGLISH_STOP_WORDS);
            SnowballAnalyzer sbAnalyzer = null;
            QueryParser queryParser = null;
            Lucene.Net.Search.Query query = null; 
            switch (expansionType)
            {
                case QueryExpansions.WordNet:
                    try
                    {
                        wordNetSearcher = new Lucene.Net.Search.IndexSearcher(Constantes.WORDNETINDEX_DIRECTORY);
                        //Lucene.Net.Analysis.Analyzer analyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer();
                        query = WorldNet.Net.SynExpand.Expand(stringQuery, wordNetSearcher, analyzer, field, 0);
                    }
                    finally
                    {
                        wordNetSearcher.Close();
                    }                    
                    break;
                case QueryExpansions.Lemmatizer:
                    queryParser = new QueryParser(field, new StandardAnalyzer());
                    sbAnalyzer = new SnowballAnalyzer("English", StopAnalyzer.ENGLISH_STOP_WORDS);
                    TokenStream tokenStream = sbAnalyzer.TokenStream(field, new System.IO.StringReader(stringQuery));//TODO check line token stream, should be field???
                    stringQuery = string.Empty;
                    while (true)
                    {
                        Lucene.Net.Analysis.Token token = tokenStream.Next();
                        if (token == null)
                            break;
                        stringQuery += token.TermText()+ " ";
                    }
                    query = queryParser.Parse(stringQuery);
                    break;
                case QueryExpansions.None:
                    queryParser = new QueryParser(field, new StandardAnalyzer());
                    query = queryParser.Parse(stringQuery);
                    break;
                default:
                    break;
            }

            Hits hits = searcher.Search(query);

            for (int i = 0; i < hits.Length(); i++)
            {
                PdfSearchResult res = new PdfSearchResult();
                res.Score = hits.Score(i);
                res.IndexId = hits.Id(i);
                Document doc = hits.Doc(i);
                res.FileName = doc.Get(INDEXFIELD_FILENAME);
                res.Path = doc.Get(INDEXFIELD_PATH);
                //res.Abstract = doc.Get(INDEXFIELD_ABSTRACT);
                res.ModifiedTime = (doc.Get(INDEXFIELD_MODIFIEDTIME) == PDFFile.INVALID_TIME) ? PDFFile.INVALID_TIME : new DateTime(long.Parse(doc.Get(INDEXFIELD_MODIFIEDTIME))).ToString();
                //res.Note = doc.Get(INDEXFIELD_NOTE);
                searchResuls.Add(res);

            }
            searcher.Close();
            return searchResuls;
        }

        public static void DeleteDocument(int indexId)
        {
            IndexReader indexReader = null;
            try
            {
                indexReader = IndexReader.Open(Constantes.INDEX_DIRECTORY);
                indexReader.DeleteDocument(indexId);               
            }
            catch (Exception ex)
            {

                throw ex;
            }
            finally
            {
                indexReader.Close();
            }
            
        }
        public static PdfSearchResult ReadDocument(int indexId)
        {
            PdfSearchResult file = null;
            IndexReader indexReader = null;
            Document doc = null;
            try
            {
                indexReader = IndexReader.Open(Constantes.INDEX_DIRECTORY);
                doc = indexReader.Document(indexId);
                file = new PdfSearchResult();
                file.FileName = doc.Get(INDEXFIELD_FILENAME);
                file.Path = doc.Get(INDEXFIELD_PATH);
                file.Abstract = doc.Get(INDEXFIELD_ABSTRACT);
                file.ModifiedTime = (doc.Get(INDEXFIELD_MODIFIEDTIME) == PDFFile.INVALID_TIME) ? PDFFile.INVALID_TIME : DateTime.FromFileTimeUtc(long.Parse(doc.Get(INDEXFIELD_MODIFIEDTIME))).ToString();
                file.Note = doc.Get(INDEXFIELD_NOTE);
                file.Contents = doc.Get(INDEXFIELD_CONTENTS);
                file.IndexId = indexId;

            }
            catch (Exception ex)
            {

                throw ex;
            }
            finally
            {
                indexReader.Close();
            }
            return file;
        }
        private static Document CreateDocument(PDFFile pdfFile)
        {
            Document luceneDocument = null;
            try
            {
                luceneDocument = new Document();
                // Add the path of the file as a pdfStripper named "path".
                // Use a pdfStripper that is indexed (i.e., searchable), but don't
                // tokenize the pdfStripper into words.
                luceneDocument.Add(new Field(INDEXFIELD_PATH, pdfFile.Path, Field.Store.YES,
                           Field.Index.UN_TOKENIZED));

                // Add the last modified date of the file to a pdfStripper named
                // "modified". Use a pdfStripper that is indexed (i.e., searchable),
                // but don't tokenize the pdfStripper into words.
                luceneDocument.Add(new Field(INDEXFIELD_MODIFIEDTIME,pdfFile.ModifiedTime,
                                Field.Store.YES, Field.Index.UN_TOKENIZED));


                luceneDocument.Add(new Field(INDEXFIELD_FILENAME, pdfFile.FileName, Field.Store.YES, Field.Index.UN_TOKENIZED));
                // Add the contents of the file to a pdfStripper named "contents".
                // Specify a Reader, so that the text of the file is tokenized
                // and indexed, but not stored. Note that FileReader expects
                // the file to be in the system's default encoding. If that's
                // not the case, searching for special characters will fail.
                luceneDocument.Add(new Field(INDEXFIELD_CONTENTS, pdfFile.Contents, Field.Store.YES, Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS));
                
                luceneDocument.Add(new Field(INDEXFIELD_ABSTRACT, pdfFile.Abstract, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS));

                luceneDocument.Add(new Field(INDEXFIELD_NOTE, pdfFile.Note, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
                
            }
            catch (Exception)
            {

                throw;
            }
            finally
            { }
            return luceneDocument;
        }
        public LuceneIndexer()
        {
            //TODO: check directory with DirectoryManager
            //TODO: cargar si existe indice de lo contrario Crea el indice
            indexOpen = false;

        }



        public void ContinueIndexFile()
        {
            if (indexOpen == false)
            {
                indexWriter = new IndexWriter(Constantes.INDEX_DIRECTORY, new StandardAnalyzer(), false);
                indexOpen = true;
            }
        }
        public void CreateNewIndexFile()
        {
            if (indexOpen == false)
            {
                indexWriter = new IndexWriter(Constantes.INDEX_DIRECTORY, new StandardAnalyzer(), true);
                indexOpen = true;
            }
        }

        public void CloseIndex()
        {
            if (indexWriter != null)
            {
                indexWriter.Close();
                indexOpen = false;
            }
        }
    }
}
